9章 スマートコントラクトセキュリティ
https://gyazo.com/44ef15d74f04c293552738344d49fe6c
P.181〜
ベストプラクティス
デザインパターン
アンチパターン
9.1 セキュリティのベストプラクティス
ミニマリズム / シンプル性
コードの再利用
コードの品質
可読性 / 監査容易性
テストカバレッジ
9.2 セキュリティリスクとアンチパターン
やっていくなら、よく勉強しよう
ここからいろいろ説明していくよ
9.3 リエントランシー
詳細はこれらを見て
9.3.1 脆弱性
再入場 (reenters)
9.3.2 予防テクニック
組み込みの transfer 関数
チェックエフェクトインタラクションパターン
ミューテックス (mutex)
9.3.3 実例:The DAO
大変なことになって、イーサリアムクラシックを生み出したハードフォークにつながった
9.4 算術オーバーフロー/アンダーフロー
256 を uint8 に格納しようとすると 0 になる
注意して格納しないとオーバーフローやアンダーフローを引き起こす
9.4.1 脆弱性
「残高をこえる値は引き出せない」のロジックでアンダーフローを起こされるとマズい
require(balances[msg.sender] - _value >= 0);
9.4.2 予防テクニック
数理ライブラリを使って加算、減算、乗算などを行う
9.4.3 実例:PoWHCとバッチトランスファーオーバーフロー(CVE-2018-10999)
866 ether (当時のレートで $800k !?) がやられた
9.5 予期せぬイーサ
コントラクトにイーサが強制的に送信されてマズいことになるケースがある
9.5.1 脆弱性
不変式検査 (invariant checking) というのがある
開発者が最初に Solidity を習得するときによくある誤解として、コントラクトが payable関数を介してのみイーサを受け取ることができるというものがあります。
コントラクトにイーサを送って balance を狂わすことで、前提を破壊できちゃう
require(this.balance == finalMileStone);
9.5.2 予防テクニック
この種の脆弱性は通常、this.balance の誤用によって発生します。可能であれば、コントラクトロジックは、コントラクトの残高の正確な値に依存しないようにする必要があります。
balance を参照せずに、自前で入出を管理する depositeWei という変数を導入する例が示されている
9.5.3 さらなる例
9.6 DELEGATECALL
9.6.1 脆弱性
自分自身のライブラリ内のコードは、安全で脆弱性のないものにすることができます。ただし、別のアプリケーションのコンテクストで実行すると、新しい脆弱性が発生する可能性があります。
delegatecall はコントラクトのコンテクストを保持する
呼び出し元と、呼び出されるライブラリのコンテクストが混ざって死ぬ
9.6.2 予防テクニック
9.6.3 実例:パリティマルチシグウォレット(2度目のハック)
Parity Multisig Hacked. Again
An In-Depth Look at the Parity Multisig Bug
認可の処理が破壊されてウォレット内のイーサが無に帰す例
9.7 デフォルトの可視性
Solidity の関数には可視性指定子がある
external
public
internal
private
デフォルトは public
9.7.1 脆弱性
デフォルトが public だから危ない
9.7.2 予防テクニック
Solidity v0.5.0 以降では、可視性指定子が設定されていない場合はコンパイルエラーとなる
9.7.3 実例:パリティマルチシグウォレット(最初のハック)
9.8 エントロピーイリュージョン
名前がかっこいい june29.icon
イーサリアムのトランザクションは決定論的
「分散エントロピーの達成」という、よく知られた問題がある
9.8.1 脆弱性
ギャンブルを表現しようとしたコントラクトが、実際には必中できてしまうようなものになってしまう
9.8.2 予防テクニック
エントロピー (ランダム性) の発生源はブロックチェーンの外に持たせる
9.8.3 実例:PRNGコントラクト
2018 年 2 月、アルセイ・レウトフは、ある種の擬似乱数生成器(PRNG)を使用していた 3,649 件のライブスマートコントラクトの分析についてブログにまとめました。彼は悪用される可能性のある 43 件のコントラクトを見つけました。
9.9 外部コントラクトの参照
外部メッセージの呼び出しによって、悪意ある攻撃者の意図が読み取りにくくなってしまう
9.9.1 脆弱性
任意のコードが実行されてしまう
9.9.2 予防テクニック
new キーワードを使用する
外部コントラクトアドレスをハードコードする
9.9.3 実例:リエントランシーハニーポット
9.10 ショートアドレス/パラメータ攻撃
Solidity コントラクト自体ではなく、それらとやり取りする可能性のあるサードパーティのアプリケーションに対して実行される
9.10.1 脆弱性
スマートコントラクトに渡されるパラメータ、短いパラメータのときに EVM が末尾をゼロフィルする 量の桁を増やしてトークンを引き出したり
0 で終わるアドレスを用意してそのアドレスでトークンを受け取ったりできてしまう
9.10.2 予防テクニック
ブロックチェーンに送信する前にパラメータを検証しよう
ゼロフィルは必ず末尾に行われるので、パラメータの順序をいい感じにしよう
9.11 未チェックのCALL戻り値
CALL と send 関数は、呼び出しが失敗したときに戻り値として false を返す
が、revert してくれるわけではないので勘違いしているとハンドリングをミスる
9.11.1 脆弱性
send 関数の失敗を考慮できていない実装の例が示される
9.11.2 予防テクニック
send じゃなくて transfer 使え
withdrawal パターンを使うともっと堅牢になるぞ
9.11.3 実例:EtherPotとKing of the Ether
宝くじ的なやつ
なにがマズかったのかの詳細
より深刻なやつ
9.12 競合状態/フロントランニング
「コントラクトの外部呼び出し」と「ブロックチェーンのマルチユーザーの性質」の組み合わせによって、予想外の状態を生み出してしまう
Ethereum Wiki の Race conditions の項目
9.12.1 脆弱性
攻撃者はトランザクションプールを監視し、誰かが得をするトランザクション T を発行したのを見つけたとき、T よりはるかに高い gasPrice を設定してトランザクションを発行し、先回りできてしまう
この先回りがフロントランニング
9.12.2 予防テクニック
gasPrice に上限を設定する
commit-reveal スキームを使う
まず情報を隠した状態でトランザクションを送って、
それがブロックに取り込まれたあとで情報を明らかにしたトランザクションを送る
「サブマリン送信」という手法が提案されている
9.12.3 実例:ERC20とBancor
ERC20 の脆弱性の解説
9.13 サービス拒否(DoS)
コントラクトを動作不能状態にできてしまう
9.13.1 脆弱性
「外部から操作されたマッピングまたは配列のループ」のパターン
公開関数を利用して配列をどんどん大きくすることで、ループの処理に必要な gas が肥大化して動作不能になる
「所有者の操作」のパターン
特権ユーザーが秘密鍵を紛失すると終わり
「外部呼び出しに基づく状態の進行」のパターン
9.13.1 予防テクニック
外から大きくできる配列をループで処理してはいけない
所有者をマルチシグにする
所定の時刻を迎えたら解放される時限式の仕組みを入れておく
9.13.2 実例:GovernMental
1,100 ether が回収不能になった
9.14 ブロックタイムスタンプ操作
不正確なブロックタイムスタンプがコントラクトで使用されると大変なことになったりする
9.14.1 脆弱性
「タイムスタンプを 15 で割った余りが 0 だったら」」という判定をしているコードの例
タイムスタンプを操作できると、その人が有利になってしまう
9.14.2 予防テクニック
タイムスタンプをエントロピー源に使わない
ICO の終了時刻を設定したい場合は、タイムスタンプの代わりにブロックナンバーを使うなどしましょう 9.14.3 実例:GovernMental
9.13.2 から引き続き、また GovernMental が槍玉に上がる
9.15 慎重なコンストラクタ
「コントラクト」と「コンストラクタ」はパッと見で判別がむつかしい… june29.icon
ここでは「コンストラクタ」の方ね
Solidity v0.4.22より前のバージョンでは、コンストラクタはコントラクトと同じ名前を持つ関数として定義されていました。このような場合、開発中にコントラクト名が変更されると、コンストラ タ名も変更されなければ、通常の呼び出し可能な関数になります。想像のとおり、これはいくつかの興味深いコントラクトハッキングにつながる可能性があります。
「興味深いハッキング」と称するの、科学者っぽくていい june29.icon
9.15.1 脆弱性
コントラクトとコンストラクタの名前が一致していなくて死ぬ
9.15.2 予防テクニック
Solidity v0.4.22 以降を使いましょう
constructor キーワードが導入されたのでそれを使えばオッケー
9.15.3 実例:Rubixi
9.16 未初期化ストレージポインタ
不適切に変数を初期化することで脆弱なコントラクトを生み出してしまうことがある
9.16.1 脆弱性
関数内のローカル変数は、その型に応じてストレージまたはメモリになる (?)
初期化されていないローカルストレージ変数には、コントラクト内の他のストレージ変数の値が含まれる可能性がある
状態変数は、コントラクトに現れた順にスロットに格納される
(あとはいっしょにコードを見よう)
9.16.2 予防テクニック
Solidity v0.5.0 以降を使おう!
初期化されていないストレージ変数があるとコンパイルエラーにしてくれる
9.16.3 実例:OpenAddressLotteryとCryptoRouletteハニーポット
OpenAddressLottery
CryptoRoulette
9.17 浮動小数点数と精度
本書日本語版翻訳時点でのバージョン Solidity v0.5.11 では、固定小数点数は完全にはサポートされていない
整数を駆使して計算を行うことになるが、気をつけないと脆弱性を生じさせてしまう
9.17.1 脆弱性
演算結果の端数が切り捨てられて取引がめちゃくちゃになる例
9.17.2 予防テクニック
この例では tokensPerEth の代わりに weiPerTokens を使う方がいい
大きい値になった方がいい
msg.value/weiPerEth*tokenPerEth より msg.value*tokenPerEth/weiPerEth の方がいい
先に乗算をやって数を大きくした方が精度が上がる
DS-Math provides arithmetic functions for the common numerical primitive types of Solidity. You can safely add, subtract, multiply, and divide uint numbers without fear of integer overflow. You can also find the minimum and maximum of two numbers.
9.17.3 実例:Ethstick
数値の精度が wei
9.18 Tx.Origin認証
Solidity のグローバル変数 tx.origin
この変数をコントラクトの認証に使用するとフィッシングのような攻撃に対して脆弱になる
9.18.1 脆弱性
「私のアドレスはこれだよ」と言って、攻撃用コントラクトのアドレスを相手に伝える
相手が攻撃用コントラクトにイーサを送ってしまう
脆弱なコントラクトの認証が突破されてイーサを引き出されてしまう
9.18.2 予防テクニック
よい子は tx.origin を認証に使わないように!
どうしても必要で使うときは require(tx.origin == msg.sender) のようにしておけばよい
9.19 コントラクトライブラリ
定評のある既存のプラットフォーム上のライブラリを使用すると、最新のアップグレードから恩恵を受けることができるなど、多くの利点があります。また、イーサリアムのライブコントラクトの総数を減らすことで費用を節約し、イーサリアムエコシステムに利益をもたらします。
9.20 まとめ
スマートコントラクトの領域で働いている開発者が理解すべきことはたくさんあります。スマートコントラクトの設計とコード作成のベストプラクティスに従うことで多くの深刻な落とし穴や罠を回避できます。
おそらく最も基本的なソフトウェアセキュリティの原則は、信頼できるコードの再利用を最大限にすることです。これは暗号学でも非常に重要で、「独自の暗号を作るな(Don’t roll your own crypto.)」という格言に要約されています。スマートコントラクトの場合、コミュニティによって徹底的にチェックされた自由に利用可能なライブラリからできるだけ多くを得る、ということになります。 hr.icon
次章!